package niu.toy.spring.support.impl;


import niu.toy.spring.mvc.DispatcherServlet;
import org.objectweb.asm.*;
import niu.toy.spring.support.ParameterNameDiscoverer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 使用生成的class里的字节码里的局部变量表来获取变量名
 */
public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {
    private static final Logger logger = LoggerFactory.getLogger(LocalVariableTableParameterNameDiscoverer.class);
    @Override
    public String[] getParameterNames(Method method){
        String methodName = method.getName();
        Class<?>[] methodParameterTypes = method.getParameterTypes();
        int methodParameterCount = methodParameterTypes.length;
        String className = method.getDeclaringClass().getName();
        boolean isStatic = Modifier.isStatic(method.getModifiers());
        String[] methodParametersNames = new String[methodParameterCount];
        try {
            ClassReader classReader = new ClassReader(className);
            classReader.accept(new ClassVisitor(Opcodes.ASM9) {
                @Override
                public MethodVisitor visitMethod(
                        final int access,
                        final String name,
                        final String descriptor,
                        final String signature,
                        final String[] exceptions) {
                    System.out.println("visitMethod:" + name);
                    final Type[] argTypes = Type.getArgumentTypes(descriptor);
                    if (!methodName.equals(name) || !matchTypes(argTypes, methodParameterTypes)) {
                        return null;
                    }

                    return new MethodVisitor(Opcodes.ASM9) {
                        @Override
                        public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
                            System.out.println("--" + name);
                            // 处理静态方法与否~~ ，非静态方法，则第一个参数是 this ,然后才是方法的参数
                            int methodParameterIndex = isStatic ? index : index - 1;
                            if (0 <= methodParameterIndex && methodParameterIndex < methodParameterCount) {
                                methodParametersNames[methodParameterIndex] = name;
                            }
                            super.visitLocalVariable(name, descriptor, signature, start, end, index);
                        }
                    };
                }
            }, ClassReader.EXPAND_FRAMES);
        }catch (Exception e){
            logger.error(className+"."+methodName+"参数解析出错:",e);
        }
        return methodParametersNames;
    }
    @Override
    public String[] getParameterNames(Constructor<?> ctr) {
        throw new UnsupportedOperationException();
    }

    /**
     * 比较参数是否一致
     */
    private static boolean matchTypes(Type[] types, Class<?>[] parameterTypes) {
        if (types.length != parameterTypes.length) {
            return false;
        }
        for (int i = 0; i < types.length; i++) {
            if (!Type.getType(parameterTypes[i]).equals(types[i])) {
                return false;
            }
        }
        return true;
    }
}
